﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using ShadeTree.Binding;
using ShadeTree.Binding.Drivers;
using ShadeTree.Binding.Elements;
using ShadeTree.WinForms.Tests.WPF;

namespace ShadeTree.WinForms.Tests
{
    public class TestDriver : ITestDriver
    {
        private static readonly List<IControlHandler> _handlers = new List<IControlHandler>();
        private readonly IScreenDriver _binder;

        static TestDriver()
        {
            _handlers.Add(new CheckboxHandler());
            _handlers.Add(new WpfCheckboxHandler());
            _handlers.Add(new RadioButtonHandler());
            _handlers.Add(new TextBoxHandler());
            _handlers.Add(new WpfTextBoxHandler());
            _handlers.Add(new ComboBoxHandler());
            _handlers.Add(new WpfComboBoxHandler());
            _handlers.Add(new LabelHandler());
            _handlers.Add(new WpfLabelHandler());
        }

        public TestDriver(IScreenDriver binder)
        {
            _binder = binder;
        }

        #region ITestDriver Members

        public void Click(string label)
        {
            findDriver(label).Click();
        }

        public bool IsChecked(string label)
        {
            IControlDriver driver = findDriver(label);
            return handlerFor(driver.Control).IsChecked(driver.Control);
        }

        public bool IsEnabled(string label)
        {
            return findDriver(label).Enabled;
        }

        public bool IsVisible(string label)
        {
            return findDriver(label).Visible;
        }

        public void Enter(string label, string text)
        {
            IControlDriver driver = findDriver(label);

            _binder.Focus(driver.Control);
            handlerFor(driver.Control).Enter(driver.Control, text);
        }

        //public void EnterDate(string label, string dateString)
        //{
        //    putFocusOnControl(label);


        //    DateTime date = DateTimeSubstitutionHandler.GetDateTime(dateString);
        //    var element = (IDateElement) findElement(label);
        //    element.EnterDate(date);
        //}


        public DateTime GetDate(string label)
        {
            var element = (IDateElement) findElement(label);
            DateTime? date = element.GetValue();

            if (!date.HasValue)
            {
                throw new ApplicationException("\n\n\nThe Date is Null!\n\n\n");
            }

            return date.Value;
        }

        public bool DateIsBlank(string label)
        {
            var element = (IDateElement) findElement(label);
            return !element.GetValue().HasValue;
        }

        public void Select(string label, string display)
        {
            putFocusOnControl(label);
            var element = (IListElement) findElement(label);
            element.SelectByDisplay(display);
        }

        public void TabOffOf(string label)
        {
            findDriver(label).FireLostFocus();
        }

        public bool IsFocused(string label)
        {
            return findDriver(label).IsFocused;
        }

        public object GetValue(string label)
        {
            IScreenElement element = findElement(label);
            return handlerFor(element.Control).GetValue(element);
        }

        public IList GetListFor(string label)
        {
            var element = (IListElement) findElement(label);
            return element.GetListOfItems();
        }

        #endregion

        public static void RegisterHandler(IControlHandler handler)
        {
            _handlers.Add(handler);
        }

        private static IControlHandler handlerFor(object control)
        {
            foreach (IControlHandler handler in _handlers)
            {
                if (handler.Handles(control))
                {
                    return handler;
                }
            }

            throw new ApplicationException("No IControlHandler can process type " + control.GetType().FullName);
        }

        private IScreenElement findElement(string label)
        {
            IScreenElement element = _binder.FindElement(label);

            if (element == null)
            {
                string message = string.Format("\n\n\nCould not find a Control with label or alias '{0}'\n\n\n", label);
                throw new ApplicationException(message);
            }

            return element;
        }

        private IControlDriver findDriver(string label)
        {
            object control = _binder.FindElement(label).Control;

            if (control == null)
            {
                string message = string.Format("\n\n\nCould not find control named {0}\n\n\n", label);
                throw new ApplicationException(message);
            }

            return ControlDriverFactory.GetDriver(control);
        }

        private void putFocusOnControl(string label)
        {
            _binder.Focus(label);
        }

        public static void FireEvent(object control, string eventName, params object[] args)
        {
            MethodInfo minfo =
                control.GetType().GetMethod("On" + eventName,
                                            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            ParameterInfo[] param = minfo.GetParameters();
            Type parameterType = param[0].ParameterType;
            minfo.Invoke(control, new[] {Activator.CreateInstance(parameterType)});
        }

        public void Click(object control)
        {
            _binder.Focus(control);
            ControlDriverFactory.GetDriver(control).Click();
        }

    }
}